Skip to content

feat(incentives): add protocol fee#1691

Merged
0xClandestine merged 5 commits intorelease-dev/incentive-councilfrom
feat/rewards-protocol-fee
Jan 12, 2026
Merged

feat(incentives): add protocol fee#1691
0xClandestine merged 5 commits intorelease-dev/incentive-councilfrom
feat/rewards-protocol-fee

Conversation

@0xClandestine
Copy link
Copy Markdown
Member

@0xClandestine 0xClandestine commented Jan 6, 2026

Motivation:

We want to add a configurable protocol fee mechanism to the RewardsCoordinator contract to enable revenue generation. This allows reward submitters to opt into paying fees while maintaining backward compatibility with existing integrations.

Modifications:

  • Added protocolFee (fixed constant amount) and feeRecipient state variables
  • Implemented setOptInProtocolFee() for submitters to opt into fee payments
  • Implemented setFeeRecipient() (owner-only) to set the fee destination address

Result:

Submitters can voluntarily enable protocol fees on their reward distributions. When opted in, the fixed fee amount is deducted and sent to the designated recipient. Existing integrations continue working unchanged since fees are disabled by default.

@0xClandestine 0xClandestine changed the title feat: add RewardsCoordinator protocol fee feat(incentives): add protocol fee Jan 6, 2026
@0xClandestine 0xClandestine requested a review from ypatil12 January 6, 2026 20:48
@0xClandestine 0xClandestine force-pushed the feat/rewards-protocol-fee branch from 8933146 to 6eac771 Compare January 7, 2026 02:08
@0xClandestine 0xClandestine marked this pull request as ready for review January 7, 2026 02:09
@0xClandestine
Copy link
Copy Markdown
Member Author

ci failing from flakey unrelated test, disregard during review plz

Base automatically changed from feat/incentives-implementation to release-dev/incentive-council January 9, 2026 17:59
@0xClandestine 0xClandestine force-pushed the feat/rewards-protocol-fee branch from 15dd5f7 to a1f9bd8 Compare January 9, 2026 19:19
@0xClandestine 0xClandestine force-pushed the feat/rewards-protocol-fee branch from a1f9bd8 to d64c649 Compare January 9, 2026 19:21
- used intra org no need for fees
uint32 _activationDelay,
uint16 _defaultSplitBips
uint16 _defaultSplitBips,
address _feeRecipient
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to use reinitializer here .. since we plan to call initialize when upgrade to set _feeRecipient .. or we plan to call the function setFeeRecipient after upgrade instead ??

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, should use reinitializer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option: Don't add a re-initializer and just call the function as part of the atomic batch tx of the upgrade.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would then need to re-add non-zero address checks. Keeping as-is for now.


// 1. Set fee recipient to address(0)
cheats.prank(rewardsCoordinator.owner());
rewardsCoordinator.setFeeRecipient(address(0));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is failing here .. because you can't set a 0 address as feeRecipient
also the logic of that test seems wrong .. since feeRecipient should never be 0 ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed, outdated test 49510b9

0, // activationDelay
0 // defaultSplitBips
0, // defaultSplitBips
address(0) // feeRecipient
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should add the feeRecipient param to both deployment scripts as well (devnet and local) .. that's why the deployFromScratch test is failing

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

@ypatil12 ypatil12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments

_validateRewardsSubmission(rewardsSubmission);

// Then transfer the full amount to the contract.
rewardsSubmission.token.safeTransferFrom(msg.sender, address(this), rewardsSubmission.amount);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on moving all of this to a helper function and we just return the new rewardSubmission, nonce, and hash? Mainly for deduplication

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do so, I'd want to cleanup the other methods as well. Right now they're implemented for readability since size isn't of massive concern.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, fine as-is then

- adding fee amount doesn't make sense, this isn't RC
@0xClandestine 0xClandestine merged commit b7a78d0 into release-dev/incentive-council Jan 12, 2026
10 of 15 checks passed
@0xClandestine 0xClandestine deleted the feat/rewards-protocol-fee branch January 12, 2026 21:06
0xClandestine added a commit that referenced this pull request Jan 14, 2026
**Motivation:**

We want to add a configurable protocol fee mechanism to the
RewardsCoordinator contract to enable revenue generation. This allows
reward submitters to opt into paying fees while maintaining backward
compatibility with existing integrations.

**Modifications:**

- Added protocolFee (fixed constant amount) and feeRecipient state
variables
- Implemented setOptInProtocolFee() for submitters to opt into fee
payments
- Implemented setFeeRecipient() (owner-only) to set the fee destination
address

**Result:**

Submitters can voluntarily enable protocol fees on their reward
distributions. When opted in, the fixed fee amount is deducted and sent
to the designated recipient. Existing integrations continue working
unchanged since fees are disabled by default.
eigenmikem added a commit that referenced this pull request Mar 2, 2026
# v1.12.0 Incentive Council (ELIP-012)

## Release Manager

@0xClandestine 

# Overview

**Core Features**

- **EmissionsController**: Mints EIGEN at a fixed inflation rate per
epoch and distributes via gauge weights (bips 0-10,000)
- **Permissionless Trigger**: Anyone can call `pressButton()` to process
epoch emissions—no trusted keeper required
- **5 Distribution Types**:
- `RewardsForAllEarners` — Protocol-wide rewards for all delegated stake
- `OperatorSetTotalStake` — Rewards proportional to total stake in
operator set
- `OperatorSetUniqueStake` — Rewards proportional to unique stake
allocations
  - `EigenDA` — Special pathway for EigenDA (pre-OperatorSets AVS)
- `Manual` — Off-chain computed distributions sent directly to
Incentives Committee
  
- **Silent Failure Handling**: Distribution failures don't block other
distributions (except reentrancy/OOG attacks)
- **Protocol Fee Mechanism**: Opt-in 20% fee on reward submissions in
RewardsCoordinator
  - Disabled by default (backward compatible)
  - Submitters opt-in via `setOptInForProtocolFee()`
  - Fee recipient configurable by owner

**Governance & Roles**

- **Protocol Council**: Sets Incentives Committee address via
`setIncentiveCouncil()`
- **Incentives Committee**: 
  - Configure distributions: `addDistribution()`, `updateDistribution()`
  - Receive swept tokens via `sweep()`
- **AVSs**: Must grant EmissionsController permission for OperatorSet
distributions

**Key Design Points**

- **Epoch-Based**: EIGEN minted once per epoch at
`EMISSIONS_INFLATION_RATE`
- **Future-Only Updates**: Distributions can only be added/updated for
future epochs
- **Immutable Config**: Inflation rate, start time, and epoch length set
at deployment
- **Pausable**: Both `pressButton()` and `sweep()` respect
`PAUSED_TOKEN_FLOWS` flag
- **Missed Epochs Skipped**: No accumulation—if `pressButton()` isn't
called during an epoch, those emissions are permanently lost

# Changelog

- feat(incentives): add interface
[#1678](#1678)
- feat(incentives): add implementation
[#1681](#1681)
- feat(incentives): add protocol fee
[#1691](#1691)
- feat(incentives): add deploy scripts
[#1699](#1699)
- fix: internal review changes
[#1703](#1703)
- feat: add EigenDA rewards submission type
[#1705](#1705)

# Scope

**New Contracts:**
- `EmissionsController.sol` — Main implementation
- `EmissionsControllerStorage.sol` — Storage layout
- `IEmissionsController.sol` — Interface

**Modified Contracts:**
- `RewardsCoordinator.sol` — Added protocol fee mechanism and
`createEigenDARewardsSubmission()`
- `RewardsCoordinatorStorage.sol` — Added `PROTOCOL_FEE_BIPS`,
`isOptedInForProtocolFee`, `feeRecipient`, `emissionsController`

---------

Co-authored-by: Rajath Alex <rajathalex@gmail.com>
Co-authored-by: eigenmikem <michael.muehl@eigenlabs.org>
Co-authored-by: ELHAJ <124453227+elhajin@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
0xClandestine added a commit that referenced this pull request Mar 5, 2026
# v1.12.0 Incentive Council (ELIP-012)

## Release Manager

@0xClandestine 

# Overview

**Core Features**

- **EmissionsController**: Mints EIGEN at a fixed inflation rate per
epoch and distributes via gauge weights (bips 0-10,000)
- **Permissionless Trigger**: Anyone can call `pressButton()` to process
epoch emissions—no trusted keeper required
- **5 Distribution Types**:
- `RewardsForAllEarners` — Protocol-wide rewards for all delegated stake
- `OperatorSetTotalStake` — Rewards proportional to total stake in
operator set
- `OperatorSetUniqueStake` — Rewards proportional to unique stake
allocations
  - `EigenDA` — Special pathway for EigenDA (pre-OperatorSets AVS)
- `Manual` — Off-chain computed distributions sent directly to
Incentives Committee
  
- **Silent Failure Handling**: Distribution failures don't block other
distributions (except reentrancy/OOG attacks)
- **Protocol Fee Mechanism**: Opt-in 20% fee on reward submissions in
RewardsCoordinator
  - Disabled by default (backward compatible)
  - Submitters opt-in via `setOptInForProtocolFee()`
  - Fee recipient configurable by owner

**Governance & Roles**

- **Protocol Council**: Sets Incentives Committee address via
`setIncentiveCouncil()`
- **Incentives Committee**: 
  - Configure distributions: `addDistribution()`, `updateDistribution()`
  - Receive swept tokens via `sweep()`
- **AVSs**: Must grant EmissionsController permission for OperatorSet
distributions

**Key Design Points**

- **Epoch-Based**: EIGEN minted once per epoch at
`EMISSIONS_INFLATION_RATE`
- **Future-Only Updates**: Distributions can only be added/updated for
future epochs
- **Immutable Config**: Inflation rate, start time, and epoch length set
at deployment
- **Pausable**: Both `pressButton()` and `sweep()` respect
`PAUSED_TOKEN_FLOWS` flag
- **Missed Epochs Skipped**: No accumulation—if `pressButton()` isn't
called during an epoch, those emissions are permanently lost

# Changelog

- feat(incentives): add interface
[#1678](#1678)
- feat(incentives): add implementation
[#1681](#1681)
- feat(incentives): add protocol fee
[#1691](#1691)
- feat(incentives): add deploy scripts
[#1699](#1699)
- fix: internal review changes
[#1703](#1703)
- feat: add EigenDA rewards submission type
[#1705](#1705)

# Scope

**New Contracts:**
- `EmissionsController.sol` — Main implementation
- `EmissionsControllerStorage.sol` — Storage layout
- `IEmissionsController.sol` — Interface

**Modified Contracts:**
- `RewardsCoordinator.sol` — Added protocol fee mechanism and
`createEigenDARewardsSubmission()`
- `RewardsCoordinatorStorage.sol` — Added `PROTOCOL_FEE_BIPS`,
`isOptedInForProtocolFee`, `feeRecipient`, `emissionsController`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants